// @HEADER
//***********************************************************************//
//    SiYuan: A numerical PDE solver                                     //
//    This Software is released under the BSD 2-Clause license detailed  //
//    in the file "LICENSE" in the top-level SiYuan directory            //
//***********************************************************************//
// @HEADER

#ifndef EVALUATOR_MATERIAL_HPP
#define EVALUATOR_MATERIAL_HPP

#include "PanzerDiscFE_config.hpp"

#include "Phalanx_Evaluator_Macros.hpp"
#include "Phalanx_MDField.hpp"
#include "Phalanx_DataLayout_DynamicLayout.hpp"
#include "Panzer_PhysicsBlock.hpp"
#include "Panzer_Evaluator_Macros.hpp"

#include "MaterialBase.hpp"

namespace SiYuan {
    
template<typename EvalT, typename Traits>
class VariableEvaluator
  : public panzer::EvaluatorWithBaseImpl<Traits>,
  public PHX::EvaluatorDerived<EvalT, Traits>
{
  public:
    VariableEvaluator(std::shared_ptr<XYZLib::variable<double>>& pval, std::string& varname, int& wksize, int& np, int& nitem);

    void postRegistrationSetup(typename Traits::SetupData d, PHX::FieldManager<Traits>& fm);
    void evaluateFields(typename Traits::EvalData d);

  private:
    using ScalarT = typename EvalT::ScalarT;

    std::shared_ptr<XYZLib::variable<double>> _value;
    PHX::MDField<ScalarT> _fieldValue;
    bool _isConstant;
    std::size_t _size;
};

/*template<typename EvalT, typename Traits>
using CMaterialEvals = std::vector< std::shared_ptr<VariableEvaluator<EvalT,Traits> > >;

template<typename EvalT, typename Traits>
void buildMaterialEvaluators( CMaterialEvals<EvalT,Traits>&  matEvals, std::vector< Teuchos::RCP<panzer::PhysicsBlock> > & physicsBlocks,
                    CMaterials& materials, std::vector<std::string> pblockNames );*/

class MaterialEvalFactoryBase : public panzer::EvaluatorsRegistrar{

  public:

    MaterialEvalFactoryBase() {}
    
    virtual ~MaterialEvalFactoryBase() {}
    
    Teuchos::RCP< std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > >
    virtual buildEval(const std::vector< Teuchos::RCP<panzer::PhysicsBlock> > & physicsBlocks,
           CMaterials& materials ) const = 0;

    /** This a convenience function for registering the evaluators. Essentially this
      * facilitates better usage of the ClosureModel TM and allows an easy registration
      * process externally without knowning the compile-time evaluation type.
      *
      * \param[in] evaluators Evaluators to register
      * \param[in] fm Field manager where the evaluators will be registered on completion.
      */
    virtual void registerEvaluators(const std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > & evaluators,
                                    PHX::FieldManager<panzer::Traits>& fm) const = 0;

  };

  template<typename EvalT>
  class MaterialEvalFactory : public MaterialEvalFactoryBase {

  protected:
    bool m_throw_if_model_not_found;
  public:

    MaterialEvalFactory(bool throw_if_model_not_found=true) : m_throw_if_model_not_found(throw_if_model_not_found) {}
    
    virtual ~MaterialEvalFactory() {}
    
    Teuchos::RCP< std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > >
    buildEval(const std::vector< Teuchos::RCP<panzer::PhysicsBlock> > & physicsBlocks,
           CMaterials& materials ) const;

    /** This a convenience function for registering the evaluators. Essentially this
      * facilitates better usage of the ClosureModel TM and allows an easy registration
      * process externally without knowning the compile-time evaluation type.
      *
      * \param[in] evaluators Evaluators to register
      * \param[in] fm Field manager where the evaluators will be registered on completion.
      */
    virtual void registerEvaluators(const std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > & evaluators,
                                    PHX::FieldManager<panzer::Traits>& fm) const
    { 
      for (std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > >::size_type i=0; i < evaluators.size(); ++i)
        this->template registerEvaluator<EvalT>(fm, evaluators[i]);
    }

    virtual void setThrowOnModelNotFound(bool do_throw) {
      m_throw_if_model_not_found=do_throw;
    }

  };

  template<typename Traits>
  class MaterialEvalFactory_TemplateManager : 
    public PHX::TemplateManager<typename Traits::EvalTypes,
				MaterialEvalFactoryBase, MaterialEvalFactory<_> > 
  {};

  class MaterialEvalFactory_TemplateBuilder {
  public:
    
    template <typename EvalT>
    Teuchos::RCP<MaterialEvalFactoryBase> build() const 
    {
      return Teuchos::rcp( static_cast<MaterialEvalFactoryBase*>(new MaterialEvalFactory<EvalT>) );
    }
    
  };

  /** The analytic solution to the mixed poisson equation for the sine source.
  */
template<typename EvalT, typename Traits>
class MaterialEvaluator : public panzer::EvaluatorWithBaseImpl<Traits>,
            public PHX::EvaluatorDerived<EvalT, Traits>
{

public:
    MaterialEvaluator(const std::string & name, const std::shared_ptr<Material> m,
                       const panzer::IntegrationRule & ir);
                                                                        
    void postRegistrationSetup(typename Traits::SetupData d,           
                               PHX::FieldManager<Traits>& fm) {};        
                                                                     
    void evaluateFields(typename Traits::EvalData d);               


protected:
  typedef typename EvalT::ScalarT ScalarT;
  PHX::MDField<ScalarT> param;

  std::string _name;           // name of property
  unsigned int _np;            // number of values;
  std::shared_ptr<Material> pmatl;
};

template<typename EvalT>
class MaterialEvaluatorFactory : public panzer::ClosureModelFactory<EvalT> {

public:

   Teuchos::RCP< std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > >
   buildClosureModels(const std::string& model_id,
          const Teuchos::ParameterList& models,
		      const panzer::FieldLayoutLibrary& fl,
		      const Teuchos::RCP<panzer::IntegrationRule>& ir,
          const Teuchos::ParameterList& default_params,
          const Teuchos::ParameterList& user_data,
		      const Teuchos::RCP<panzer::GlobalData>& global_data,
          PHX::FieldManager<panzer::Traits>& fm) const;

};

/*
  Some generally used material parameters here. Problem specified ones would defined with problems'
  equationset define
*/
template<typename EvalT, typename Traits>
class Density : public MaterialEvaluator<EvalT, Traits>
{
  public:
    Density(const std::shared_ptr<Material> m, const panzer::IntegrationRule & ir)
    : MaterialEvaluator<EvalT, Traits>(std::string("Density"),m,ir) 
    {this->setName(std::string("Density"));};

    //void evaluateFields(typename Traits::EvalData d);
};

}

#include "MaterialEvaluator_impl.hpp"

#endif
